﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Web;

namespace ChatDLuXService
{
    public class Utilities
    {
        // Function to return connection string.
        public static string ConnectionString()
        {
            return Properties.Settings.Default.ConnectionString;
        }

        // Creates command object to READ data from database.
        public DataTable ReadData(string StoredProcedure, SqlParameter[] Parameters)
        {
            try
            {
                // Open database connection
                SqlConnection conn = new SqlConnection(ConnectionString());
                conn.Open();

                // Create command object
                SqlCommand cmd = new SqlCommand(StoredProcedure, conn);
                cmd.CommandType = CommandType.StoredProcedure;

                // Add parameter list to command object
                if (Parameters != null)
                {
                    foreach (SqlParameter parameter in Parameters)
                    {
                        cmd.Parameters.Add(parameter);
                    }
                }

                // Fill data table
                SqlDataAdapter da = new SqlDataAdapter(cmd);
                DataSet ds = new DataSet();
                da.Fill(ds);
                DataTable dt = new DataTable();
                dt = ds.Tables[0];
                conn.Close();
                return dt;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                return null;
            }
        }

        public void ExecuteProcedure(string StoredProcedure, SqlParameter[] Parameters)
        {
            // Open database connection
            SqlConnection conn = new SqlConnection(ConnectionString());
            conn.Open();

            // Create command object
            SqlCommand cmd = new SqlCommand(StoredProcedure, conn);
            cmd.CommandType = CommandType.StoredProcedure;

            // Add parameter list to command object
            foreach (SqlParameter parameter in Parameters)
            {
                cmd.Parameters.Add(parameter);
            }

            // Execute stored procedure
            cmd.ExecuteNonQuery();
            conn.Close();
        }

        public bool CheckExists(string StoredProcedure, SqlParameter[] Parameters)
        {
            // Open Db conn
            SqlConnection conn = new SqlConnection(ConnectionString());
            conn.Open();

            //Create command object
            SqlCommand cmd = new SqlCommand(StoredProcedure, conn);
            cmd.CommandType = CommandType.StoredProcedure;

            // Add output parameter to command object
            SqlParameter output = new SqlParameter("@Result", SqlDbType.Bit);
            output.Direction = ParameterDirection.Output;
            cmd.Parameters.Add(output);

            // Add parameter list to command object
            foreach (SqlParameter parameter in Parameters)
            {
                cmd.Parameters.Add(parameter);
            }

            // Execute stored procedure
            cmd.ExecuteNonQuery();
            conn.Close();

            // Get true or false
            bool result = (bool)cmd.Parameters["@Result"].Value;
            //bool result = (bool)output.Value;

            return result;
        }

        /***
         * Generates a random salt value to be used when hashing a users password
         ***/
        public byte[] GenerateSalt(int size)
        {
            // Generate a cryptographic random number
            RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
            byte[] salt = new byte[size];
            rng.GetBytes(salt);

            // Return a Base64 string representation of the random number.
            return salt;
        }

        /***
         * Algorith that hashes a password with the provided salt byte array
         * ***/
        public byte[] GenerateHash(string password, byte[] salt)
        {
            //return GenerateHash(Encoding.UTF8.GetBytes(password), salt);
            byte[] pwd = Encoding.UTF8.GetBytes(password);
            var saltedPassword = new byte[password.Length + salt.Length];
            pwd.CopyTo(saltedPassword, 0);
            salt.CopyTo(saltedPassword, password.Length);

            return new SHA256Managed().ComputeHash(saltedPassword);
        }

        public DataTable GetLoginInfo(string Username, string Password)
        {
            DataTable dt = new DataTable();
            
            // Get the salt value for the username provided
            byte[] salt = GetSalt(Username);

            // Generate the hash value with the password provided and the salt value retrieved from db
            byte[] hash = GenerateHash(Password, salt);

            // Compare generated hash with the username's hash in the database,
            // if credentials match user info is returned otherwise an empty datatable is returned.
            SqlParameter[] parameters = new SqlParameter[]
            {
                new SqlParameter("@Username", Username),
                new SqlParameter("@Password", hash)
            };
            dt = ReadData("chat_dev.spGetLoginInfo", parameters);
            return dt;
        }

        /***
         * Gets the salt value for the username provided from the database
         * ***/
        private byte[] GetSalt(string Username)
        {
            byte[] salt = new byte[0];
            SqlParameter[] parameters = new SqlParameter[]
            {
                new SqlParameter("@Username", Username)
            };
            DataTable dt = ReadData("chat_dev.spGetSalt", parameters);
            foreach (DataRow r in dt.Rows)
            {
                salt = (byte[])r["Salt"];
            }
            return salt;
        }
    }
}